/*****************************************************
	Nome: 		VISEDc Core Module
	Versão:		3.5

	Funcionalidades:
	- Controle de Execução
	- Processamento de mensagens ao usuário

 *****************************************************/

#include <ctime>
#include <iostream>
#define _snprintf snprintf
/*
#include "DEFS.H"
#include "CORE.H"
#include "MODEL.H"
#include "MED.H"
#include "INI.H"
#include "DAT.H"*/

#include "misc/DEFS.H"
#include "CORE.H"
#include "dat/INI.H"
//#include "UTIL.H"
//#include <time.h>
#include <sys/time.h>

char Buf[BUF_SIZE];					// Buffer de texto global

CORE Core;

using namespace std;

int main( int argc, char* argv[] ) {

	Core.Log( LOGO, NEWLINE, NEWLINE );					// Imprime o nome do programa
	Core.GetInput( argc, argv );						// Carrega o nome do arquivo de entrada

	Core.Run();

	return 0;								// Esta linha nunca deverá ser executada
}

void CORE::Run() {

	Model.Initialize( this );
	Dat.Initialize( this );

	strcat( GetFileName( Buf, BUF_SIZE ), INI_EXT );
	//GetFileName( Buf, BUF_SIZE );
	Load_INI( Buf );
	Med.Initialize( this );

	strcat( GetFileName( Buf, BUF_SIZE ), DAT_EXT );
	//GetFileName( Buf, BUF_SIZE );
	Dat.Create( Buf );

    int i=0;
    //std::cout << i << "%";//<< std::endl;
    fflush(stdout);


    //novo
    struct timeval start, atual;
    long etime, seconds, useconds,t_est;
    gettimeofday(&start, NULL);
    //novo temporizador

    int precisaoProc=1000;
    bool alterado=false;
    std::cout << "Completo(%)\tTempo de Processamento\tTempo Restante\n";
    bool fim=false;
    if (Med.qualDetCol!=4)
    {
        #pragma omp parallel if (!Med.tudoSeq)
        {
            while (!fim)
            {
                Med.StepForward();
                #pragma omp single
                {
                    if ((Model.DAT_Delay() >= Dat.Resolution)) Dat.Write_Frame();
                    if (Model.Time >= Dat.RecTime) fim=true;

                    //Abaixo uma implementação de progresso e tempo estimado até o final
                    if ((int)(((Model.Time/Dat.RecTime)*precisaoProc)) != i)
                    {
                        i++ ;
                        gettimeofday(&atual, NULL);
                        etime=atual.tv_sec  - start.tv_sec;
                        t_est=(etime*Dat.RecTime)/Model.Time;
                        int estimado=t_est-etime;
                        if ((!alterado)&&((estimado/3600)==0))
                        {
                            alterado=true;
                            precisaoProc=100;
                            i/=10;
                        }
                        std::cout << "\r    " << i/10 ;
                        if(precisaoProc==1000)std::cout << '.' ;
                        std::cout << i%10 <<"%\t\t      ";
                        std::cout << (etime)/3600 << 'h'<< ((etime)/60)%60 << 'm'<< (etime)%60 << "s\t\t ";
                        std::cout <<(estimado)/3600 << 'h'<< ((estimado)/60)%60 << 'm'<< (estimado)%60 << "s   "; //<< std::endl;
                        fflush(stdout);
                    }
                }
            }
        }
    }
    else
    {
        //#pragma omp parallel if (!Med.tudoSeq)
        {
            while (!fim)
            {
                Med.StepForwardTudoNaColisao();
                //#pragma omp barrier
                //#pragma omp single
                {
                    if ((Model.DAT_Delay() >= Dat.Resolution)) Dat.Write_Frame();
                    if (Model.Time >= Dat.RecTime) fim=true;

                    //Abaixo uma implementação de progresso e tempo estimado até o final
                    if ((int)(((Model.Time/Dat.RecTime)*precisaoProc)) != i)
                    {
                        i++ ;
                        gettimeofday(&atual, NULL);
                        etime=atual.tv_sec  - start.tv_sec;
                        t_est=(etime*Dat.RecTime)/Model.Time;
                        int estimado=t_est-etime;
                        if ((!alterado)&&((estimado/3600)==0))
                        {
                            alterado=true;
                            precisaoProc=100;
                            i/=10;
                        }
                        std::cout << "\r    " << i/10 ;
                        if(precisaoProc==1000)std::cout << '.' ;
                        std::cout << i%10 <<"%\t\t      ";
                        std::cout << (etime)/3600 << 'h'<< ((etime)/60)%60 << 'm'<< (etime)%60 << "s\t\t ";
                        std::cout <<(estimado)/3600 << 'h'<< ((estimado)/60)%60 << 'm'<< (estimado)%60 << "s   "; //<< std::endl;
                        fflush(stdout);
                    }
                }
            }
        }
    }

	//novo
	gettimeofday(&atual, NULL);
    seconds  = atual.tv_sec  - start.tv_sec;
    useconds = atual.tv_usec - start.tv_usec;
    etime = (((seconds) * 1000 + useconds/1000.0) + 0.5);
    if (etime>100000)
    {
        etime/=1000;
        std::cout << "\n\nTempo de Processamento: "<<(etime)/3600 << 'h'<< ((etime)/60)%60 << 'm'<< (etime)%60 <<'s' << std::endl;
    }
    else std::cout << "\n\nTempo de Processamento: "<< (float)etime/1000 << " s" << std::endl;
    //novo


	Save_INI();

	Model.Terminate();
	Med.Terminate();
	Dat.Terminate();
}




void CORE::GetInput( int argc, char* argv[] ) {			// Extrai o nome de arquivo da linha de comando

	if (argc >= 2)
	{
	    bool arquivoGuardado=false;
	    for (int i=1;i<argc;i++)
	    {
	        if ((argv[i][0]=='-'))
	        {

	            switch(argv[i][1])
	            {
	                case 'h':
                            cout<< "Ajuda:\n";
                            cout<< "uso:  ./med <opções> nomedoarquivo\n";
                            cout<< "Opções:\n";
                            cout << "\t-D : Na detecção de colisão Contato:\n";
                            cout << "\t\ts : Usar Screening (default)\n";
                            cout << "\t\te : Usar Screening com cálculos embutidos\n";
                            cout << "\t\tm : Usar Munjiza-Minato\n";
                            cout << "\t\tn : Usar Munjiza-NBS\n";
                            cout << "\n\t-I : Na Integração usar:\n";
                            cout << "\t\tr : Runge Kutta 4ta Ordem\n";
                            cout << "\t\te : Euler (default)\n";
                            cout << "\t\tv : Verlet\n";
                            cout << "\n\t-S : Cálculo totalmente sequencial\n";
                            cout << "\t-f : Cálculo de Fronteira Sequencial\n";
                            cout << "\t-m : Cálculo de Molas Sequencial\n";
                            cout << "\t-i : Cálculo de Integração Sequencial\n";
                            cout << "\t-d : Cálculo de Detecção de Colisão Sequencial\n";
                            cout << "\t-n : Cálculo de Forças Independentes (Arrasto,Gravidade,etc) Sequencial\n";
                            cout << "\t-r : Cálculo de Restrições Sequencial\n";
                            cout<< "\n\n\tObs:.Por padrão tudo é feito em paralelo\n\n";
                            cout<< "\tExemplo: ./med -S -Dn arquivo.ini (Tudo Sequencial com Munjiza-NBS)\n\n";
                            abort();
                            break;

                    case 'D':
                            switch(argv[i][2])
                            {
                                case 's':

                                        Med.qualDetCol=1;
                                        break;
                                case 'e':
                                        Med.qualDetCol=4;
                                        break;
                                case 'm':
                                        Med.qualDetCol=2;
                                        break;
                                case 'n':
                                        Med.qualDetCol=3;
                                        break;

                                default:
                                        printf("Opção de algoritmo de Contato inválida\n");
                                        abort();
                                        break;
                            }
                            break;
                    case 'I':
                            switch(argv[i][2])
                            {
                                case 'r':
                                        Med.qualIntegracao=3;
                                        break;
                                case 'e':
                                        Med.qualIntegracao=1;
                                        break;
                                case 'v':
                                        Med.qualIntegracao=2;
                                        break;
                                default:
                                        printf("Opção de algoritmo de Integração inválida\n");
                                        abort();
                                        break;
                            }
                            break;

                    case 'n':
                            Med.indepSeq=true;
                            break;
                    case 'i':
                            Med.intSeq=true;
                            break;
                    case 'd':
                            Med.detSeq=true;
                            break;
                    case 'f':
                            Med.frontSeq=true;
                            break;
                    case 'M':
                            switch(argv[i][2])
                            {
                                case 's':
                                        Med.qualOpcMola=1;
                                        break;
                                case 'p':
                                        Med.qualOpcMola=2;
                                        break;
                                case 'c':
                                        Med.qualOpcMola=3;
                                        break;
                                default:
                                        printf("Opção de algoritmo de Molas inválida\n");
                                        abort();
                                        break;
                            }
                            break;
                    case 'r':
                            Med.restrSeq=true;
                            break;
                    case 'S':
                            Med.tudoSeq=true;
                            break;
                    default:
                            cout <<"Ignorando entrada desconhecida: " << argv[i] << '\n';
                            break;
	            }

	        }
	        else if(argv[i][0]=='-')
	        {
                            cout <<"Ignorando entrada desconhecida: " << argv[i] << '\n';
	        }
	        else if (!arquivoGuardado)
	        {
	            arquivoGuardado=true;
	            strcpy( FileName, argv[i] );
	        }
	        else
	        {
                 cout <<"Ignorando entrada desconhecida: " << argv[i] << '\n';
	        }
	    }
	}
	else
	{
	    Error("Qual o nome do arquivo??\n");
	   // abort();
    }

    if ((Med.qualOpcMola!=1) && (Med.tudoSeq))
    {
        Med.qualOpcMola=1;
    }

	char* ext = strrchr( FileName, '.' );
	if (ext == NULL) Error("Qual o nome do arquivo??\n");						// Caractere não encontrado
	*ext = '\0';

	Log( "Lendo o arquivo: " , ext, ".INI\n" );
}

char* CORE::GetFileName( char* buf, int size ) {		// Armazena o nome de arquivo no buffer

	return strncpy( buf, FileName, size-1 );
}

void CORE::Log( string s1, string s2, string s3 ) {		// Registra uma ocorrência

	if (!s1.empty()) cout <<  s1 ;
	if (!s2.empty()) cout <<  s2 ;
	if (!s3.empty()) cout <<  s3 ;
}

void CORE::Error( string msg, int row ) {			// Exibe uma mensagem de erro e termina

	if (row == 0) Log( msg, NEWLINE );
	else { snprintf( Buf, BUF_SIZE, "%s (linha %i)\n", msg.c_str(), row ); Log( Buf ); }
	Model.Terminate();
	Med.Terminate();
	Dat.Terminate();
	exit(1);
}
